/**
 * @file src/MotifTreeElement.cpp
 *
 * @date 28-aug-2013
 *
 * @author Youri Hoogstrate
 *
 * @section LICENSE
 * yh-kt-fold can predict RNA 2D structures including K-turns.
 * Copyright (C) 2012-2013 Youri Hoogstrate
 *
 * This file is part of yh-kt-fold.
 *
 * yh-kt-fold is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * yh-kt-fold is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */



#include "MotifTreeElement.h"



/**
 * @brief
 *
 * @date 25-jul-2013
 */
MotifTreeElement::MotifTreeElement(Motif *arg_motif)
{
	this->motif = arg_motif;
	
	this->link_5p_size_smaller = NULL;
	this->link_5p_size_larger = NULL;
	
	this->link_5p_motif_smaller = NULL;
	this->link_5p_motif_larger = NULL;
	
	
	this->link_3p_size_smaller = NULL;
	this->link_3p_size_larger = NULL;
	
	this->link_3p_motif_smaller = NULL;
	this->link_3p_motif_larger = NULL;
}



/**
 * @brief
 *
 * @date 26-jul-2013
 */
MotifTreeElement::~MotifTreeElement()
{
	delete this->motif;
	
	delete this->link_3p_motif_smaller;
	delete this->link_3p_motif_larger;
	
	delete this->link_3p_size_smaller;
	delete this->link_3p_size_larger;
	
	
	delete this->link_5p_motif_smaller;
	delete this->link_5p_motif_larger;
	
	delete this->link_5p_size_smaller;
	delete this->link_5p_size_larger;
}



/**
 * @brief Adds a Motif (as member of a MotifTreeElement) to another element of the tree.
 *
 * @date 28-aug-2013
 */
void MotifTreeElement::add_motif(Motif *arg_motif, int &arg_search_type)
{
	//printf(" - Received by [%s], stage: %i\n", this->motif->name.c_str(), arg_search_type);
	
	unsigned int i;
	char self;
	char arg;
	
	switch(arg_search_type)
	{
		case 1:
			if(this->motif->get_length(FIVE_PRIME) > arg_motif->get_length(FIVE_PRIME))
			{
				if(this->link_5p_size_smaller != NULL)
				{
					this->link_5p_size_smaller->add_motif(arg_motif, arg_search_type);
				}
				else
				{
					//printf("  *1 Became new 5p_smaller of [%s]\n", this->motif->name.c_str());
					
					MotifTreeElement *element;
					element = new MotifTreeElement(arg_motif);
					this->link_5p_size_smaller = element;
				}
			}
			else if(arg_motif->get_length(FIVE_PRIME) > this->motif->get_length(FIVE_PRIME))
			{
				if(this->link_5p_size_larger != NULL)
				{
					this->link_5p_size_larger->add_motif(arg_motif, arg_search_type);
				}
				else
				{
					//printf("  *1 Became new 5p_larger\n of [%s]\n", this->motif->name.c_str());
					MotifTreeElement *element;
					element = new MotifTreeElement(arg_motif);
					this->link_5p_size_larger = element;
				}
			}
			else
			{
				/** @todo replace by: this->add_motif(arg_motif, arg_search_type+1); */
				arg_search_type++;
				this->add_motif(arg_motif, arg_search_type);
			}
			break;
			
		case 2:
			arg = 'A';
			self = 'A';
			i = 0;
			
			while(arg == self && i < this->motif->get_length(FIVE_PRIME))
			{
				self = this->motif->motif5p[i];
				arg = arg_motif->motif5p[i];
				
				if(arg < self)
				{
					if(this->link_5p_motif_smaller != NULL)
					{
						this->link_5p_motif_smaller->add_motif(arg_motif, arg_search_type);
					}
					else
					{
						//printf("  *2 Became new 5p_smaller of [%s]\n", this->motif->name.c_str());
						MotifTreeElement *element;
						element = new MotifTreeElement(arg_motif);
						this->link_5p_motif_smaller = element;
					}
				}
				else if(arg > self)
				{
					if(this->link_5p_motif_larger != NULL)
					{
						this->link_5p_motif_larger->add_motif(arg_motif, arg_search_type);
					}
					else
					{
						//printf("  *2 Became new 5p_larger\n of [%s]\n", this->motif->name.c_str());
						MotifTreeElement *element;
						element = new MotifTreeElement(arg_motif);
						this->link_5p_motif_larger = element;
					}
				}
				
				i++;
			}
			
			if(arg == self)
			{
				/** @todo replace by: this->add_motif(arg_motif, arg_search_type+1); */
				arg_search_type++;
				this->add_motif(arg_motif, arg_search_type);
			}
			
			break;
		case 3:
			if(this->motif->get_length(THREE_PRIME) > arg_motif->get_length(THREE_PRIME))
			{
				if(this->link_3p_size_smaller != NULL)
				{
					this->link_3p_size_smaller->add_motif(arg_motif, arg_search_type);
				}
				else
				{
					//printf("  *1 Became new 3p_smaller of [%s]\n", this->motif->name.c_str());
					
					MotifTreeElement *element;
					element = new MotifTreeElement(arg_motif);
					this->link_3p_size_smaller = element;
				}
			}
			else if(arg_motif->get_length(THREE_PRIME) > this->motif->get_length(THREE_PRIME))
			{
				if(this->link_3p_size_larger != NULL)
				{
					this->link_3p_size_larger->add_motif(arg_motif, arg_search_type);
				}
				else
				{
					//printf("  *1 Became new 3p_larger\n of [%s]\n", this->motif->name.c_str());
					MotifTreeElement *element;
					element = new MotifTreeElement(arg_motif);
					this->link_3p_size_larger = element;
				}
			}
			else
			{
				/** @todo replace by: this->add_motif(arg_motif, arg_search_type+1);*/
				arg_search_type++;
				this->add_motif(arg_motif, arg_search_type);
			}
			break;
		case 4:
			arg = 'A';
			self = 'A';
			i = 0;
			
			while(arg == self && i < this->motif->get_length(THREE_PRIME))
			{
				self = this->motif->motif3p[i];
				arg = arg_motif->motif3p[i];
				
				if(arg < self)
				{
					if(this->link_3p_motif_smaller != NULL)
					{
						this->link_3p_motif_smaller->add_motif(arg_motif, arg_search_type);
					}
					else
					{
						//printf("  *2 Became new 3p_smaller of [%s]\n", this->motif->name.c_str());
						MotifTreeElement *element;
						element = new MotifTreeElement(arg_motif);
						this->link_3p_motif_smaller = element;
					}
				}
				else if(arg > self)
				{
					if(this->link_3p_motif_larger != NULL)
					{
						this->link_3p_motif_larger->add_motif(arg_motif, arg_search_type);
					}
					else
					{
						//printf("  *2 Became new 3p_larger\n of [%s]\n", this->motif->name.c_str());
						MotifTreeElement *element;
						element = new MotifTreeElement(arg_motif);
						this->link_3p_motif_larger = element;
					}
				}
				
				i++;
			}
			
			if(arg == self)
			{
				fprintf(stderr, "Fatal error: adding the following motif multiple times to the Tree:\n 5' %s\n 3' %s\n",arg_motif->motif5p.c_str(),arg_motif->motif3p.c_str());
				exit(1);
			}
			break;
	}
}



/**
 * @brief Finds whether or not there is a motif in the tree with the "5p" and "3p" sequences as given.
 *
 * @date 07-aug-2013
 *
 * @todo Split up into 4 sub-functions?
 */
Motif *MotifTreeElement::search_motif(const std::string arg_motif5p, const std::string arg_motif3p, int &arg_search_type)
{
	Motif *m = NULL;
	
	unsigned int i;
	char self;
	char arg;
	
	switch(arg_search_type)
	{
		case 1:
			if(this->motif->get_length(FIVE_PRIME) > arg_motif5p.size())
			{
				if(this->link_5p_size_smaller != NULL)
				{
					m = this->link_5p_size_smaller->search_motif(arg_motif5p, arg_motif3p, arg_search_type);
				}
			}
			else if(arg_motif5p.size() > this->motif->get_length(FIVE_PRIME))
			{
				if(this->link_5p_size_larger != NULL)
				{
					m = this->link_5p_size_larger->search_motif(arg_motif5p, arg_motif3p, arg_search_type);
				}
			}
			else
			{
				arg_search_type++;
				m = this->search_motif(arg_motif5p, arg_motif3p, arg_search_type);
			}
			break;
			
		case 2:
			arg = 'A';
			self = 'A';
			i = 0;
			
			while(arg == self && i < this->motif->get_length(FIVE_PRIME))
			{
				self = this->motif->motif5p[i];
				arg = arg_motif5p[i];
				
				if(arg < self)
				{
					if(this->link_5p_motif_smaller != NULL)
					{
						m = this->link_5p_motif_smaller->search_motif(arg_motif5p, arg_motif3p, arg_search_type);
					}
				}
				else if(arg > self)
				{
					if(this->link_5p_motif_larger != NULL)
					{
						m = this->link_5p_motif_larger->search_motif(arg_motif5p, arg_motif3p, arg_search_type);
					}
				}
				
				i++;
			}
			
			if(arg == self)
			{
				arg_search_type++;
				m = this->search_motif(arg_motif5p, arg_motif3p, arg_search_type);
			}
			
			break;
		case 3:
			if(this->motif->get_length(THREE_PRIME) > arg_motif3p.size())
			{
				if(this->link_3p_size_smaller != NULL)
				{
					m = this->link_3p_size_smaller->search_motif(arg_motif5p, arg_motif3p, arg_search_type);
				}
			}
			else if(arg_motif3p.size() > this->motif->get_length(THREE_PRIME))
			{
				if(this->link_3p_size_larger != NULL)
				{
					m = this->link_3p_size_larger->search_motif(arg_motif5p, arg_motif3p, arg_search_type);
				}
			}
			else
			{
				arg_search_type++;
				m = this->search_motif(arg_motif5p, arg_motif3p, arg_search_type);
			}
			break;
		case 4:
			arg = 'A';
			self = 'A';
			i = 0;
			
			while(arg == self && i < this->motif->get_length(THREE_PRIME))
			{
				self = this->motif->motif3p[i];
				arg = arg_motif3p[i];
				
				if(arg < self)
				{
					if(this->link_3p_motif_smaller != NULL)
					{
						m = this->link_3p_motif_smaller->search_motif(arg_motif5p, arg_motif3p, arg_search_type);
					}
				}
				else if(arg > self)
				{
					if(this->link_3p_motif_larger != NULL)
					{
						m = this->link_3p_motif_larger->search_motif(arg_motif5p, arg_motif3p, arg_search_type);
					}
				}
				
				i++;
			}
			
			if(arg == self)
			{
				m = this->motif;
			}
			break;
	}
	
	return m;
}
